package com.katesoft.scale4j.web.support;

import com.katesoft.scale4j.persistent.client.LocalHibernateDaoSupport;
import com.katesoft.scale4j.web.exceptions.LocaleNotFoundException;
import com.katesoft.scale4j.web.exceptions.RoleNotFoundException;
import com.katesoft.scale4j.web.model.Role;
import com.katesoft.scale4j.web.model.User;
import com.katesoft.scale4j.web.model.UserLocale;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import static com.katesoft.scale4j.common.utils.AssertUtility.assertNotNull;

/**
 * This class is placeholder of {@link com.katesoft.scale4j.persistent.client.LocalHibernateTemplate} and default implementation of IWebappSupportOperations
 * interface.
 * <p/>
 * Convenient super class for Hibernate-based data access objects within application services.
 *
 * @author kate2007
 */
public class WebappDaoSupport extends LocalHibernateDaoSupport implements IWebappSupportOperations, UserDetailsService
{
    /*
     * (non-Javadoc)
     * @see com.katesoft.scale4j.persistent.spring.IWebappSupportOperations #getLoggedInUser()
     */
    @Override
    public User getLoggedInUser()
    {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            return null;
        }
        return loadUserByUsername(authentication.getName());
    }

    /*
    * (non-Javadoc)
    * @see com.katesoft.scale4j.persistent.spring.IWebappSupportOperations
    * #loadUser(com.katesoft.scale4j.web.security.IUserDetails)
    */
    @Override
    public User loadUser(String username)
    {
        User user = username != null ? loadUserByUsername(username) : getLoggedInUser();
        if (user == null) {
            throw new UsernameNotFoundException("User is not bound to thread");
        }
        return user;
    }

    /*
    * (non-Javadoc)
    * @see com.katesoft.scale4j.persistent.spring.IWebappSupportOperations #loadUserLocale(java.lang.String)
    */
    @Override
    public UserLocale loadLocale(final String code)
    {
        assertNotNull(code, "ILocale code can't be null");
        return getLocalHibernateTemplate().execute(new HibernateCallback<UserLocale>()
        {
            @Override
            public UserLocale doInHibernate(final Session session) throws HibernateException
            {
                Criteria criteria = session.createCriteria(UserLocale.class);
                criteria.add(Restrictions.eq(UserLocale.PROP_CODE, code));
                UserLocale locale = (UserLocale) criteria.uniqueResult();
                if (locale == null) {
                    throw new LocaleNotFoundException(code);
                }
                Hibernate.initialize(locale);
                return locale;
            }
        });
    }

    @Override
    public User loadUserByUsername(final String username) throws UsernameNotFoundException, DataAccessException
    {
        assertNotNull(username, "username can't be null");
        return getLocalHibernateTemplate().execute(new HibernateCallback<User>()
        {
            @Override
            public User doInHibernate(final Session session) throws HibernateException
            {
                Criteria criteria = session.createCriteria(User.class);
                criteria.add(Restrictions.eq(User.PROP_LOGIN, username));
                User u = (User) criteria.uniqueResult();
                if (u == null) {
                    throw new UsernameNotFoundException(username);
                }
                return initializeUserProxy(u);
            }
        });
    }

    /*
    * (non-Javadoc)
    * @see com.katesoft.scale4j.persistent.spring.IWebappSupportOperations #loadRoleByName(java.lang.String)
    */
    @Override
    public Role loadRoleByName(final String role)
    {
        assertNotNull(role, "role can't be null");
        return getLocalHibernateTemplate().execute(new HibernateCallback<Role>()
        {
            @Override
            public Role doInHibernate(Session session) throws HibernateException
            {
                Criteria criteria = session.createCriteria(Role.class);
                criteria.add(Restrictions.eq(Role.PROP_ROLE, role));
                Role r = (Role) criteria.uniqueResult();
                if (r == null) {
                    throw new RoleNotFoundException(role);
                }
                Hibernate.initialize(r);
                return r;
            }
        });
    }

    /**
     * forces dirty load of user as well as user roles.
     *
     * @param user target
     * @return fully loaded user with loaded relations.
     */
    protected User initializeUserProxy(User user)
    {
        assertNotNull(user, "User param can't not be null");
        user.forceAttributesLoad();
        if (user.getRoles() != null) {
            for (Role role : user.getRoles()) {
                Hibernate.initialize(role);
            }
        }
        return user;
    }
}
